﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web.Services.Description;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Xml.Serialization;
using System.Web.Services.Protocols;

namespace IronTrishul.Discovery
{
    class WSDLParser
    {
        public object getproxy(byte[] wsdl)
        {
            // generate CodeDom from WSDL
            ServiceDescription sd = ServiceDescription.Read(new MemoryStream(wsdl));
            ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
            importer.ServiceDescriptions.Add(sd);
            CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
            CodeNamespace codeNamespace = new CodeNamespace("");
            codeCompileUnit.Namespaces.Add(codeNamespace);
            importer.CodeGenerationOptions = CodeGenerationOptions.GenerateNewAsync | CodeGenerationOptions.GenerateOldAsync;
            importer.Import(codeNamespace, codeCompileUnit);

            // update web service proxy CodeDom tree to add dynamic support
            string wsProxyTypeName = FindProxyTypeAndAugmentCodeDom(codeNamespace);
            // compile CodeDom tree into an Assembly
            CodeDomProvider provider = CodeDomProvider.CreateProvider("CS");
            CompilerParameters compilerParams = new CompilerParameters();
            compilerParams.GenerateInMemory = true;
            compilerParams.IncludeDebugInformation = false;
            compilerParams.ReferencedAssemblies.Add(typeof(Microsoft.Scripting.Hosting.ScriptRuntime).Assembly.Location); //DLR
            CompilerResults results = provider.CompileAssemblyFromDom(compilerParams, codeCompileUnit);
            Assembly generatedAssembly = results.CompiledAssembly;

            // find the type derived from SoapHttpClientProtocol
            Type wsProxyType = generatedAssembly.GetType(wsProxyTypeName);

            if (wsProxyType == null)
            {
                throw new InvalidOperationException("Web service proxy type not generated.");
            }

            // create an instance of the web proxy type
            return Activator.CreateInstance(wsProxyType);
        }
        string FindProxyTypeAndAugmentCodeDom(CodeNamespace codeNamespace)
        {
            // add new type containing Type properties for each type
            // in the web service proxy assembly (kind of a namespace)
            string nsName = string.Format("ws_namespace_{0:x}", Guid.NewGuid().GetHashCode());
            CodeTypeDeclaration nsType = new CodeTypeDeclaration(nsName);

            CodeTypeDeclaration wsType = null; // the web service type (only one)

            foreach (CodeTypeDeclaration t in codeNamespace.Types)
            {
                string name = t.Name;

                // find the one derived from SoapHttpClientProtocol
                foreach (CodeTypeReference baseType in t.BaseTypes)
                {
                    if (baseType.BaseType == typeof(SoapHttpClientProtocol).FullName)
                    {
                        if (wsType != null)
                        {
                            throw new InvalidDataException("Found more than one web service proxy type.");
                        }

                        wsType = t;
                    }
                }

                // add the corresponding property to the namespace type
                CodeMemberProperty p = new CodeMemberProperty();
                p.Attributes &= ~MemberAttributes.AccessMask;
                p.Attributes |= MemberAttributes.Public;
                p.Name = name; // same as type name
                p.Type = new CodeTypeReference(typeof(Type));
                p.GetStatements.Add(new CodeMethodReturnStatement(new CodeTypeOfExpression(name)));
                nsType.Members.Add(p);
            }

            if (wsType == null)
            {
                // must have exactly one ws proxy
                throw new InvalidDataException("Web service proxy type not found.");
            }

            codeNamespace.Types.Add(nsType);

            // add ServiceNamespace property of the above type to the proxy type
            CodeMemberField nsField = new CodeMemberField(nsName, "_serviceNamespace");
            nsField.Attributes &= ~MemberAttributes.AccessMask;
            nsField.Attributes |= MemberAttributes.Private;
            nsField.InitExpression = new CodeObjectCreateExpression(nsName);
            wsType.Members.Add(nsField);

            CodeMemberProperty nsProp = new CodeMemberProperty();
            nsProp.Attributes &= ~MemberAttributes.AccessMask;
            nsProp.Attributes |= MemberAttributes.Public;
            nsProp.Name = "ServiceNamespace";
            nsProp.Type = new CodeTypeReference(nsName);
            nsProp.GetStatements.Add(new CodeMethodReturnStatement(
                new CodePropertyReferenceExpression(
                    new CodeThisReferenceExpression(),
                    "_serviceNamespace")));
            wsType.Members.Add(nsProp);

            // return the proxy type name
            return wsType.Name;
        }


    }
}
